/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pa165.dominatingspecies.dao.impl;

import pa165.dominating.species.dao.AnimalDAO;
import java.io.Serializable;
import javax.persistence.Query;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import org.springframework.stereotype.Repository;
import pa165.dominatingspecies.dao.common.Validator;
import pa165.dominating.species.entities.Animal;
import pa165.dominating.species.entities.Environment;
import pa165.dominating.species.exceptions.DataStorageException;
import pa165.dominating.species.exceptions.EntityException;

/**
 *
 * @author Filip Dinh
 */
@Repository
public class AnimalDAOImpl implements AnimalDAO, Serializable {

    @PersistenceContext
    private EntityManager entityManager;

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public void setEntityManager(final EntityManager em) {
        this.entityManager = em;
    }

    @Override
    public void createAnimal(Animal animal) {
        Validator.validateEntityManager(entityManager);
        Validator.validateNewEntity(animal);
        try {
            entityManager.persist(animal);
        } catch (PersistenceException e) {
            throw new DataStorageException("An error has occurred while creating new animal", e);
        }
    }

    @Override
    public void updateAnimal(Animal animal) {
        Validator.validateEntityManager(entityManager);
        Validator.validateUpdatedEntity(animal);

        try {

            Animal persistentAnimal = entityManager.find(Animal.class, animal.getId());
            if (persistentAnimal == null) {
                throw new EntityException("Animal with id " + animal.getId() + " has not been found in the databse");
            }
            Set<Animal> preyOld = persistentAnimal.getPrey();
            Set<Animal> preyNew = animal.getPrey();
            Set<Animal> attachedPreyNew = new HashSet<>();
            for (Animal preyNewAnimalToAttach : preyNew) {
                preyNewAnimalToAttach = entityManager.getReference(preyNewAnimalToAttach.getClass(), preyNewAnimalToAttach.getId());
                attachedPreyNew.add(preyNewAnimalToAttach);
            }
            preyNew = attachedPreyNew;
            animal.setPrey(preyNew);
            entityManager.merge(animal);

        } catch (PersistenceException e) {
            throw new DataStorageException("An error has occurred while updating animal", e);
        }
    }

    @Override
    public void deleteAnimal(Animal animal) {
        Validator.validateEntityManager(entityManager);
        Validator.validateDeleteEntity(animal);


        try {
            Animal existingAnimal = entityManager.find(Animal.class, animal.getId());
            if (existingAnimal == null) {
                throw new IllegalArgumentException("Animal with id " + animal.getId() + " has not been found in the databse");
            }
            entityManager.remove(existingAnimal);
        } catch (PersistenceException e) {
            throw new DataStorageException("An error has occurred while deleting animal", e);
        }


    }

    @Override
    public List<Animal> getAllAnimals() {
        Validator.validateEntityManager(entityManager);
        try {
            Query q = entityManager.createQuery("select object(o) from Animal as o");
            return q.getResultList();
        } catch (PersistenceException e) {
            throw new DataStorageException("An error has occurred while retrieving animals", e);
        }
    }

    @Override
    public Animal getAnimalById(Long id) {
        Validator.validateEntityManager(entityManager);
        Validator.validateGetEntity(id);
        try {
            return entityManager.find(Animal.class, id);
        } catch (PersistenceException e) {
            throw new DataStorageException("An error has occured when looking for a animal entity in database.", e);
        }
    }

    @Override
    public int getAnimalCount() {
        Validator.validateEntityManager(entityManager);
        try {
            Query q = entityManager.createQuery("select count(o) from Animal as o");
            return ((Long) q.getSingleResult()).intValue();
        } catch (PersistenceException e) {
            throw new DataStorageException("An error has occurred while retrieving animal count", e);
        }
    }

    @Override
    public void addPreyToPredator(Animal predator, Animal prey) {
        Validator.validateEntityManager(entityManager);
        Validator.validateUpdatedEntity(predator);
        Validator.validateUpdatedEntity(prey);
        try {

            predator.addPrey(prey);
            entityManager.merge(predator);
        } catch (PersistenceException e) {
            throw new DataStorageException("An error has occurred while adding prey to predator", e);
        }
    }

    @Override
    public void removePreyOfPredator(Animal predator, Animal prey) {
        Validator.validateEntityManager(entityManager);
        Validator.validateUpdatedEntity(predator);
        Validator.validateUpdatedEntity(prey);
        try {
            predator.removePrey(prey);
            entityManager.merge(predator);
        } catch (PersistenceException e) {
            throw new DataStorageException("An error has occurred while removing prey of predator", e);
        }
    }

    @Override
    public List<Animal> getPredatorsOfPrey(Animal prey) {
        Validator.validateEntityManager(entityManager);
        Validator.validateUpdatedEntity(prey);
        try {
            Query q = entityManager.createQuery("SELECT a FROM Animal a JOIN a.prey p WHERE p.id = :id");
            q.setParameter("id", prey.getId());
            return q.getResultList();
        } catch (PersistenceException e) {
            throw new DataStorageException("An error has occurred while retrieving predators of prey", e);
        }
    }

    @Override
    public List<Animal> getPreyOfPredator(Animal predator) {
        Validator.validateEntityManager(entityManager);
        Validator.validateUpdatedEntity(predator);


        try {
            Query q = entityManager.createQuery("SELECT p FROM Animal a JOIN a.prey p WHERE a.id = :id");
            q.setParameter("id", predator.getId());
            return q.getResultList();
        } catch (PersistenceException e) {
            throw new DataStorageException("An error has occurred while retrieving prey of predator", e);
        }
    }

    @Override
    public List<Animal> getAnimalsByEnvironment(Environment environment) {
        Validator.validateEntityManager(entityManager);
        Validator.validateUpdatedEntity(environment);
        try {
            Query q = entityManager.createQuery("select object(o) from Animal as o WHERE o.environment.id = :eid");
            q.setParameter("eid", environment.getId());
            return q.getResultList();
        } catch (PersistenceException e) {
            throw new DataStorageException("An error has occurred while retrieving animals by environment", e);
        }
    }
}
